package com.example.blemulticondemo;

import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothGatt;
import android.bluetooth.BluetoothGattCallback;
import android.bluetooth.BluetoothGattCharacteristic;
import android.bluetooth.BluetoothGattDescriptor;
import android.bluetooth.BluetoothGattService;
import android.bluetooth.BluetoothManager;
import android.bluetooth.BluetoothProfile;
import android.bluetooth.le.BluetoothLeScanner;
import android.bluetooth.le.ScanCallback;
import android.bluetooth.le.ScanResult;
import android.bluetooth.le.ScanSettings;
import android.content.Context;
import android.os.Build;
import android.os.Handler;
import android.os.Looper;
import android.text.TextUtils;
import android.util.Log;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.UUID;

/**
 * @author linvisf
 * @date 2024/8/6.
 * description：
 */
public class BleManager {
    private static final String TAG = "BleManager";

    //此属性一般不用修改
    private static final String UUID_NOTIFY_D = "00002902-0000-1000-8000-00805f9b34fb";

    // UUIDs
    private final UUID[] serviceUUIDs = {
            UUID.fromString("00006688-0000-1000-8000-00805F9B34FB"),
            UUID.fromString("00010203-0405-0607-0809-0a0b0c0d1910"),
            UUID.fromString("55535343-fe7d-4ae5-8fa9-9fafd205e455"),
            UUID.fromString("6e400001-b5a3-f393-e0a9-e50e24dcca9e"),//420
            UUID.fromString("0000fff0-0000-1000-8000-00805f9b34fb")
    };

    private final UUID[] writeUUIDs = {
            UUID.fromString("00008888-0000-1000-8000-00805F9B34FB"),
            UUID.fromString("00010203-0405-0607-0809-0a0b0c0d2b10"),
            UUID.fromString("49535343-8841-43f4-a8d4-ecbe34729bb3"),
            UUID.fromString("6e400002-b5a3-f393-e0a9-e50e24dcca9e"),//420
            UUID.fromString("0000fff2-0000-1000-8000-00805f9b34fb")
    };

    private final UUID[] notifyUUIDs = {
            UUID.fromString("0000666-0000-1000-8000-00805F9B34FB"),
            UUID.fromString("00010203-0405-0607-0809-0a0b0c0d2b10"),
            UUID.fromString("49535343-1e4d-4bd9-ba61-23c647249616"),
            UUID.fromString("6e400003-b5a3-f393-e0a9-e50e24dcca9e"),//420
            UUID.fromString("0000fff1-0000-1000-8000-00805f9b34fb")
    };

    //默认扫描时间：5s
    private static final int SCAN_TIME = 5000;

    private Handler handler = new Handler(Looper.getMainLooper());
    private Context context;
    private static BleManager instance;
    private BluetoothAdapter mAdapter;
    private BluetoothLeScanner mBluetoothLeScanner;
    private List<BluetoothDevice> scannedDevices = new ArrayList<>();
    private Map<BluetoothDevice, BluetoothGatt> gattMap = new HashMap<>();
    private Set<BluetoothDevice> connectedDevices = new HashSet<>();
    private BleManagerCallback bleManagerCallback;
    private boolean isScanning = false;

    private BleManager(Context context) {
        this.context = context;
        BluetoothManager bm = (BluetoothManager) context.getSystemService(Context.BLUETOOTH_SERVICE);
        mAdapter = bm.getAdapter();
        if (mAdapter != null) {
            mBluetoothLeScanner = mAdapter.getBluetoothLeScanner();
        }
    }

    public static BleManager getInstance(Context context) {
        if (null == instance) {
            synchronized (BleManager.class) {
                if (null == instance) {
                    instance = new BleManager(context);
                }
            }
        }
        return instance;
    }

    public void setBleManagerCallback(BleManagerCallback bleManagerCallback) {
        this.bleManagerCallback = bleManagerCallback;
    }

    // 是否关闭蓝牙
    public boolean isCloseBle() {
        return mAdapter == null || !mAdapter.isEnabled();
    }

    public void startScan(long time) {
        Log.e(TAG, "=== startScan ===");
        if (isCloseBle()) return;

        if (mBluetoothLeScanner == null && mAdapter != null) {
            mBluetoothLeScanner = mAdapter.getBluetoothLeScanner();
        }

        if (isScanning) {
            Log.e(TAG, "蓝牙扫描在进行中， 停止之前的扫描。");
            stopScan();
        }

        ScanSettings settings = new ScanSettings.Builder()
                .setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY)
                .build();

        scannedDevices.clear();
        mBluetoothLeScanner.startScan(null, settings, scanCallback);
        isScanning = true;
        handler.postDelayed(this::stopScan, time <= 0 ? SCAN_TIME : time);
    }

    public void stopScan() {
        if (mBluetoothLeScanner != null) {
            mBluetoothLeScanner.stopScan(scanCallback);
            isScanning = false;
        }
    }

    public List<BluetoothDevice> getScannedDevices() {
        List<BluetoothDevice> allDevices = new ArrayList<>(scannedDevices);
        // 这样做的目的，防止重新扫描时，没有把之前已连接的设备添加上
        for (BluetoothDevice device : connectedDevices) {
            if (!allDevices.contains(device)) {
                allDevices.add(device);
            }
        }
        return allDevices;
    }

    private final ScanCallback scanCallback = new ScanCallback() {
        @Override
        public void onScanResult(int callbackType, ScanResult result) {
//            Log.e(TAG, "=== onScanResult ===");
            BluetoothDevice device = result.getDevice();
            if (device != null && device.getAddress() != null) {
//                Log.e(TAG, "=== onScanResult ===    name =>" + device.getName() + ", mac = " + device.getAddress());
                if (!TextUtils.isEmpty(device.getName())) {
                    if (!scannedDevices.contains(device)) {
                        scannedDevices.add(device);
                        if (bleManagerCallback != null) {
                            bleManagerCallback.onDeviceFound(device);
                        }
                    }
                }
            }
        }

        @Override
        public void onBatchScanResults(List<ScanResult> results) {
//            Log.e(TAG, "=== onBatchScanResults ===");
            for (ScanResult result : results) {
                BluetoothDevice device = result.getDevice();
                if (device != null && device.getAddress() != null) {
//                    Log.e(TAG, "=== onBatchScanResults ===    name =>" + device.getName() + ", mac = " + device.getAddress());
                    if (!TextUtils.isEmpty(device.getName())) {
                        if (!scannedDevices.contains(device)) {
                            scannedDevices.add(device);
                            if (bleManagerCallback != null) {
                                bleManagerCallback.onDeviceFound(device);
                            }
                        }
                    }
                }
            }
        }

        @Override
        public void onScanFailed(int errorCode) {
            Log.e(TAG, "=== onScanFailed ===    errorCode =>" + errorCode);
            if (bleManagerCallback != null) bleManagerCallback.onScanFailed(errorCode);
        }
    };

    public List<BluetoothDevice> getConnectedDevices() {
        return new ArrayList<>(connectedDevices);
    }

    public void connectDevice(BluetoothDevice device) {
        if (connectedDevices.contains(device)) {
            Log.e(TAG, device.getName() + "蓝牙设备已连接...");
            return;
        }

        BluetoothGatt gatt;
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            gatt = device.connectGatt(context, false, gattCallback, BluetoothDevice.TRANSPORT_LE);
        } else {
            gatt = device.connectGatt(context, false, gattCallback);
        }
        gattMap.put(device, gatt);
        connectedDevices.add(device);
    }

    // 断开连接
    public void disconnect(BluetoothDevice device) {
        BluetoothGatt gatt = gattMap.get(device);
        if (gatt != null) {
            gatt.disconnect();
//            gatt.close(); // 这里不能直接关闭，否则不执行gattCallback的onConnectionStateChange方法
//            gattMap.remove(device);
//            connectedDevices.remove(device);
        }
    }

    // 判断蓝牙设备是否已连接
    public boolean isDeviceConnected(BluetoothDevice device) {
        return connectedDevices.contains(device);
    }

    // 发送数据
    public void sendData(BluetoothDevice device, byte[] data) {
        BluetoothGatt gatt = gattMap.get(device);
        if (gatt == null) {
            Log.e(TAG, "该设备未连接 => " + device.getAddress());
            return;
        }

        boolean sendState = false;

        for (int i = 0; i < serviceUUIDs.length; i++) {
            UUID serviceUUID = serviceUUIDs[i];
            UUID writeUUID = writeUUIDs[i];
            UUID notifyUUID = notifyUUIDs[i];

            BluetoothGattService service = gatt.getService(serviceUUID);
            if (service == null) {
//                Log.e(TAG, "未找到服务 => " + serviceUUID.toString());
                continue;
            }

            BluetoothGattCharacteristic characteristic = service.getCharacteristic(writeUUID);
            if (characteristic != null) {
                characteristic.setValue(data);
                //不需要来回的确认消息, 提高数据传输速度
                characteristic.setWriteType(BluetoothGattCharacteristic.WRITE_TYPE_NO_RESPONSE);
                boolean b = gatt.writeCharacteristic(characteristic);
                Log.e(TAG, "发送状态 = " + b + ", 设备：" + device.getName() + " sendData：" + bytesToHexString(data));
                sendState = true;
            } else {
//                Log.e(TAG, "未找到write特征 = " + writeUUID.toString());
            }

            BluetoothGattCharacteristic notifyCharacteristic = service.getCharacteristic(notifyUUID);
            if (notifyCharacteristic != null) {
                boolean notifyResult = gatt.setCharacteristicNotification(notifyCharacteristic, true);
                Log.e(TAG, "设置通知状态: " + notifyResult);

                BluetoothGattDescriptor descriptor = notifyCharacteristic.getDescriptor(UUID.fromString(UUID_NOTIFY_D));
                if (descriptor != null) {
                    descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
                    boolean descriptorWriteResult = gatt.writeDescriptor(descriptor);
//                    Log.e(TAG, "写描述符状态: " + descriptorWriteResult);
                } else {
//                    Log.e(TAG, "未找到描述符: " + UUID_NOTIFY_D);
                }
            } else {
//                Log.e(TAG, "未找到通知特征: " + notifyUUID);
            }

            // 如果数据已发送成功，则跳出循环
            if (sendState) break;
        }

        if (!sendState) {
            Log.e(TAG, "发送数据失败: 未找到适配的服务或特征");
        }
    }

    private final BluetoothGattCallback gattCallback = new BluetoothGattCallback() {

        @Override
        public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {//1
            // 处理连接状态改变的逻辑
            Log.e(TAG, "=== onConnectionStateChange ===  status = " + status + "; newState = " + newState);
            if (newState == BluetoothProfile.STATE_CONNECTED) {
                Log.e(TAG, "=== 连接gatt服务 ===");
                gatt.discoverServices();
                if (bleManagerCallback != null)
                    bleManagerCallback.onDeviceConnected(gatt.getDevice());
            } else if (newState == BluetoothProfile.STATE_DISCONNECTED) {
                Log.e(TAG, "=== 断开gatt服务 ===");
                gatt.close();
                gattMap.remove(gatt.getDevice());
                connectedDevices.remove(gatt.getDevice());
                if (bleManagerCallback != null)
                    bleManagerCallback.onDeviceDisconnected(gatt.getDevice());
            }
        }

        //服务被发现了
        @Override
        public void onServicesDiscovered(BluetoothGatt gatt, int status) {//2
            Log.e(TAG, "=== onServicesDiscovered ===  status = " + status);
            // 发现服务的逻辑
            if (status == BluetoothGatt.GATT_SUCCESS) {
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
                    //设置最大传输单元
                    gatt.requestMtu(250);
                }
                bleManagerCallback.onServicesDiscovered(gatt);
            }
        }

        @Override
        public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
            super.onCharacteristicRead(gatt, characteristic, status);
            Log.e(TAG, "=== onCharacteristicRead ===");
        }

        //发送数据结果
        @Override
        public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
            super.onCharacteristicWrite(gatt, characteristic, status);
            Log.e(TAG, "=== onCharacteristicWrite ===");
        }

        //收到数据
        @Override
        public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {//3
            super.onCharacteristicChanged(gatt, characteristic);
            Log.e(TAG, "=== onCharacteristicChanged ===");
            // 接收数据的逻辑
            if (bleManagerCallback != null) {
                bleManagerCallback.onDataReceived(gatt.getDevice(), characteristic.getValue());
            }
        }

        //描述符被读了
        @Override
        public void onDescriptorRead(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) {
            super.onDescriptorRead(gatt, descriptor, status);
            Log.e(TAG, "=== onDescriptorRead ===");
        }

        //描述符被写了
        @Override
        public void onDescriptorWrite(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) {
            super.onDescriptorWrite(gatt, descriptor, status);
            Log.e(TAG, "=== onDescriptorWrite ===");
        }


        @Override
        public void onMtuChanged(BluetoothGatt gatt, int mtu, int status) {
            super.onMtuChanged(gatt, mtu, status);
            if (status == BluetoothGatt.GATT_SUCCESS) {
                Log.e(TAG, "=== onMtuChanged ===    mtu =>" + mtu);
            }
        }
    };

    public interface BleManagerCallback {
        void onDeviceFound(BluetoothDevice device);

        void onScanFailed(int errorCode);

        void onDeviceConnected(BluetoothDevice device);

        void onDeviceDisconnected(BluetoothDevice device);

        void onServicesDiscovered(BluetoothGatt gatt);

        void onDataReceived(BluetoothDevice device, byte[] data);
    }

    /**
     * 将byte数组转为16进制字符串 此方法主要目的为方便Log的显示
     */
    public String bytesToHexString(byte[] src) {
        StringBuilder stringBuilder = new StringBuilder("");
        if (src == null || src.length <= 0) {
            return null;
        }
        for (byte aSrc : src) {
            int v = aSrc & 0xFF;
            String hv = Integer.toHexString(v);
            if (hv.length() < 2) {
                stringBuilder.append(0);
            }
            stringBuilder.append(hv.toUpperCase()).append(" ");
        }
        return stringBuilder.toString();
    }
}
